package com.pd.kit;

import com.alibaba.fastjson.JSONObject;
import org.apache.http.*;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.*;

/**
 * Created by dong peng on 2018/1/29.
 */
public class HttpClientUtil {

	private static final Logger log = LoggerFactory.getLogger(HttpClientUtil.class);

	private static String DEFAULT_CHARSET = "UTF-8";

	private static int DEFAULT_PORT = 80;

	private static String EMPTY = "";

	/**
	 * http get请求
	 *
	 * @param url 请求的地址
	 * @return 响应结果(字符串)
	 */
	public static String get(String url) {
		return get(url, DEFAULT_CHARSET);
	}

	/**
	 * http get请求
	 *
	 * @param url 请求的地址
	 * @param charSet 字符串编码格式
	 * @return 响应结果(字符串)
	 */
	public static String get(String url, String charSet) {
		String responseStr = null;
		CloseableHttpClient httpclient = HttpClients.createDefault();

		try {
			HttpGet httpGet = new HttpGet(url);
			CloseableHttpResponse response = httpclient.execute(httpGet);

			try {
				HttpEntity entity = response.getEntity();

				if (entity != null) {
					responseStr = EntityUtils.toString(entity, charSet);
				}
			} finally {
				response.close();
			}
		} catch (ClientProtocolException e) {
			log.error("Http get request error", e);
		} catch (IOException e) {
			log.error("Http get request error", e);
		} catch (Exception e) {
			log.error("Http get request error", e);
		} finally {
			try {
				httpclient.close();
			} catch (IOException e) {
				log.error("Httpclient close error", e);
			}
		}

		return responseStr;
	}

	/**
	 * http提交post请求
	 *
	 * @param url 请求的地址
	 * @param paramterMap 请求的参数集合
	 * @return 响应结果(字符串)
	 * @throws Exception
	 */
	public static String postForm(String url, Map<String, String> paramterMap) {
		return postForm(url, paramterMap, DEFAULT_CHARSET);
	}

	/**
	 * http提交post请求   以表单形式
	 *
	 * @param url 请求的地址
	 * @param paramterMap 请求的参数集合
	 * @param charSet 字符集
	 * @return 响应结果(字符串)
	 * @throws Exception
	 */
	public static String postForm(String url, Map<String, String> paramterMap, String charSet) {
		String responseStr = null;
		CloseableHttpClient httpClient = HttpClients.createDefault();
		HttpPost httpPost = new HttpPost(url);
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		Set<Map.Entry<String, String>> set = paramterMap.entrySet();

		for (Map.Entry<String, String> entry : set) {
			params.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
		}

		try {
			UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(params, charSet);
			httpPost.setEntity(uefEntity);
			CloseableHttpResponse response = httpClient.execute(httpPost);

			try {
				HttpEntity entity = response.getEntity();

				if (entity != null) {
					responseStr = EntityUtils.toString(entity, charSet);
				}
			} finally {
				response.close();
			}
		} catch (ClientProtocolException e) {
			log.error("Http post request error", e);
		} catch (UnsupportedEncodingException e) {
			log.error("Http post request error", e);
		} catch (IOException e) {
			log.error("Http post request error", e);
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				log.error("Httpclient close error", e);
			}
		}

		return responseStr;
	}


	public static String postXml(String url, String data) {
		return postXml(url,data,DEFAULT_CHARSET);
	}

	/**
	 * xml格式请求
	 * @param url
	 * @param data
	 * @param charSet
	 * @return
	 */
	public static String postXml(String url, String data, String charSet) {
		String responseStr = null;
		CloseableHttpClient httpClient = HttpClients.createDefault();

		// todo   如果需要绕过ssl认证
		/*
		CloseableHttpClient httpClient = null;
		try {
			httpClient = HttpClients.custom().setConnectionManager(bulidSkipSSL()).build();
		} catch (Exception e) {
			e.printStackTrace();
		}
		*/

		HttpPost httpPost = new HttpPost(url);
		StringEntity postEntity = new StringEntity(data,charSet);
		httpPost.addHeader("Content-Type", "text/xml");
		httpPost.setEntity(postEntity);

		RequestConfig requestConfig = RequestConfig.custom()
				.setSocketTimeout(5000)
				.setConnectTimeout(5000)
				.setConnectionRequestTimeout(5000)
				.build();
		httpPost.setConfig(requestConfig) ;
		try {

			CloseableHttpResponse response = httpClient.execute(httpPost);

			try {
				HttpEntity entity = response.getEntity();

				if (entity != null) {
					responseStr = EntityUtils.toString(entity, charSet);
				}
			} finally {
				response.close();
			}
		} catch (ClientProtocolException e) {
			log.error("Http post request error", e);
		} catch (UnsupportedEncodingException e) {
			log.error("Http post request error", e);
		} catch (IOException e) {
			log.error("Http post request error", e);
		} finally {
			try {
				httpClient.close();
			} catch (IOException e) {
				log.error("Httpclient close error", e);
			}
		}

		return responseStr;
	}

	/**
	 * 需要ssl证书则用测方法
	 * @return
	 * @throws Exception
	 */
	private static BasicHttpClientConnectionManager bulidSSL() throws Exception {

		// todo 初始化证书密码和路径
		// 证书
		String cretPasswd = "";
		String certPath ="";
		InputStream cretStream = null;
		KeyStore keyStore = KeyStore.getInstance("PKCS12");
		keyStore.load(cretStream,cretPasswd.toCharArray());

		//工厂实例化密钥库
		KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
		kmf.init(keyStore,cretPasswd.toCharArray());

		// SSLContext
		SSLContext sslContext = SSLContext.getInstance("TLS");
		sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());

		SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
				sslContext,
				new String[]{"TLSv1"},
				null,
				new DefaultHostnameVerifier());

		BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(
				RegistryBuilder.<ConnectionSocketFactory>create()
						.register("http", PlainConnectionSocketFactory.getSocketFactory())
						.register("https", sslConnectionSocketFactory)
						.build(),
				null,
				null,
				null
		);

		return  connManager;
	}

	/**
	 * 绕过ssl认证
	 *
	 * 实际这部分httpclient 默认bulid中已经构建了
	 *
	 * @return
	 * @throws Exception
	 */
	public static BasicHttpClientConnectionManager bulidSkipSSL() throws Exception {
		BasicHttpClientConnectionManager connManager = new BasicHttpClientConnectionManager(
				RegistryBuilder.<ConnectionSocketFactory>create()
						.register("http", PlainConnectionSocketFactory.getSocketFactory())
						.register("https", SSLConnectionSocketFactory.getSocketFactory())
						.build(),
				null,
				null,
				null
		);

		return  connManager;
	}

	/**
	 * http提交purge请求
	 *
	 * @param url
	 * @return
	 */
	public static String purge(String varnishIp, String url) {
		String responseStr = null;
		CloseableHttpClient httpclient = HttpClients.createDefault();

		try {
			HttpPurge httpPurge = new HttpPurge(url);
			RequestConfig requestConfig = RequestConfig.custom().setProxy(new HttpHost(varnishIp, DEFAULT_PORT)).build();
			httpPurge.setConfig(requestConfig);
			CloseableHttpResponse response = httpclient.execute(httpPurge);

			try {
				HttpEntity entity = response.getEntity();

				if (entity != null) {
					responseStr = EntityUtils.toString(entity, DEFAULT_CHARSET);
				}
			} finally {
				response.close();
			}
		} catch (ClientProtocolException e) {
			log.error("Http purge request error", e);
		} catch (IOException e) {
			log.error("Http purge request error", e);
		} catch (Exception e) {
			log.error("Http get request error", e);
		} finally {
			try {
				httpclient.close();
			} catch (IOException e) {
				log.error("Httpclient close error", e);
			}
		}

		return responseStr;
	}

	static class HttpPurge extends HttpRequestBase {
		public final static String METHOD_NAME = "PURGE";

		public HttpPurge() {
			super();
		}

		public HttpPurge(final URI uri) {
			super();
			setURI(uri);
		}

		public HttpPurge(final String uri) {
			super();
			setURI(URI.create(uri));
		}

		@Override
		public String getMethod() {
			return METHOD_NAME;
		}

	}

	/**
	 * @function 1.0.6 上传文件
	 * @param url 处理文件的url
	 * @param filePath	文件物理路径
	 * @param params 	额外的请求参数	不需要则设置为null
	 * @return json		返回参数信息({"msg":"succ","status":"200","url":"/group1/M00/2B/D5/rBD-H1aXG7GEUKnXAAAAAPIZOpI121.png"})
	 * @throws IOException
	 * @throws ClientProtocolException
	 */
	public static JSONObject uploadFile(final String url, final String filePath, Map<String,Object> params) throws ClientProtocolException, IOException{
		File file = loadFile(filePath);
		return uploadFile(url, file, params);
	}


	private static JSONObject uploadFile(final String url, final File file, Map<String,Object> params) throws ClientProtocolException, IOException{
		if(!file.exists() || !file.isFile()){
			throw new IllegalArgumentException("上传文件不存在!");
		}

		MultipartEntityBuilder reqBuilder = MultipartEntityBuilder.create().setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
		if(params != null && params.size() > 0){
			for (Map.Entry<String, Object> entry : params.entrySet()) {
				if(null != entry.getKey() && !EMPTY.equals(entry.getKey())){
					reqBuilder.addTextBody(entry.getKey(), String.valueOf(entry.getValue()),ContentType.APPLICATION_JSON);
				}
			}
		}

		reqBuilder.addPart("file", new FileBody(file));
		HttpEntity reqEntity = reqBuilder.build();

		CloseableHttpClient httpclient = HttpClientBuilder.create().build();
		HttpPost method = new HttpPost(url);
		method.setHeaders(createCommonHeader());
		method.setEntity(reqEntity);
		HttpResponse response = httpclient.execute(method);

		if(response != null){
			if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
				String resultTxt = EntityUtils.toString(response.getEntity());
				JSONObject resultJson = JSONObject.parseObject(resultTxt);
				return resultJson;
			}
		}

		return null;
	}


	/**
	 * @function 返回需要传输的文件,并且验证是否存在
	 * @param filePath	文件的物理路径
	 * @return	返回文件信息
	 */
	private static File loadFile(final String filePath){
		File file = new File(filePath);
		if(!file.exists() || !file.isFile()){
			throw new IllegalArgumentException("上传文件不存在!");
		}

		return file;
	}

	private static  Header[] createCommonHeader() {
		Header[] headers = new Header[3];
		long timestamp = System.currentTimeMillis();
		headers[0] = new BasicHeader("timestamp", timestamp + EMPTY);
		headers[1] = new BasicHeader("charset", "UTF-8");
		headers[2] = new BasicHeader("Accept", "application/json");
		return headers;
	}

	/**
	 * @function 上传文件的地址必须按照这个格式构造
	 * @function 获取请求地址
	 * @return	返回全路径
	 */
	private static String getRequestUrl(){
		Long timeStamp =  System.currentTimeMillis();
		StringBuffer url = new StringBuffer("http://www.baidu.cn" + "/upload");
		url.append("?");
		url.append("sign=");
		url.append(getSin(timeStamp));
		url.append("&");
		url.append("channel=");
		url.append("10001");
		url.append("&");
		url.append("timestamp=");
		url.append(timeStamp + EMPTY);
		return url.toString();
	}

	private static String getSin(Long timeStamp){
		String seed = EMPTY + 10001 + timeStamp + "token";
		return MD5.createSign(seed);
	}

	/**
	 * 图片展示地址：http://p1.aaa.cn/group1/M00/2B/D5/rBD-H1aXG7GEUKnXAAAAAPIZOpI121.png
	 * @param args
	 * @throws ClientProtocolException
	 * @throws IOException
	 */
	public static void main(String[] args) throws ClientProtocolException, IOException {
		String url = getRequestUrl();
		Map<String,Object> map = new HashMap<String,Object>();
		map.put("name", "111");
		map.put("age", 24);
		JSONObject result = uploadFile(url,"E:/ip.png",null);
		System.out.println(result.get("url"));
	}
}
